{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src='http://www-scf.usc.edu/~ghasemig/images/sharif.png' alt=\"SUT logo\" width=200 height=200 align=left class=\"saturate\" >\n",
    "\n",
    "<br>\n",
    "<font face=\"Times New Roman\">\n",
    "<div dir=ltr align=center>\n",
    "<font color=0F5298 size=7>\n",
    "    Introduction to Machine Learning <br>\n",
    "<font color=2565AE size=5>\n",
    "    Computer Engineering Department <br>\n",
    "    Fall 2022<br>\n",
    "<font color=3C99D size=5>\n",
    "    Homework 4: Practical - Convolutional Neural Networks <br>\n",
    "<font color=696880 size=4>\n",
    "    Arian Amani\n",
    "    \n",
    "    \n",
    "____\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Full Name : \n",
    "### Student Number : \n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Problem\n",
    "This assignment uses PyTorch to build and implement a residual CNN for solving a classification problem. Our goal is to classify handwritten digits from 0 to 9 on the MNIST dataset. You can try larger and more fun datasets if you have access to Google Colab. (for example: [facial emotion detection](https://www.kaggle.com/datasets/msambare/fer2013))\n",
    "\n",
    "* It is highly recommended to run this notebook on Google Colab so that you can utilize its GPU.\n",
    "* If you need to change the inputs of functions you are implementing, or want to add new cells or functions, feel free to do so."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "W0bGV8AXO3Yx"
   },
   "source": [
    "# Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "CdrZr6HWO2p8"
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from torch.nn import functional as F\n",
    "from torch.utils.data import Dataset, DataLoader, random_split\n",
    "from torchvision import transforms, datasets\n",
    "\n",
    "from tqdm import tqdm\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.style.use('ggplot')\n",
    "###########################################################\n",
    "##  If you need any other packages, import them below    ##\n",
    "###########################################################\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "device"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gzydGC2SNuAu"
   },
   "source": [
    "# Prepare The Data (10 Points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Write a composed transformation to transform our dataset's PIL images to tensors and normalize them with ```mean: 0.1307``` and ```std: 0.3081```: "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Hint</summary>\n",
    "<br>\n",
    "<a href=\"https://pytorch.org/vision/stable/generated/torchvision.transforms.Compose.html\">torchvision.transforms.Compose</a>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "###############################################################\n",
    "##                Compose your transformations               ##\n",
    "###############################################################\n",
    "transformation = None # Write your code here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ANdYnd04N0ze"
   },
   "source": [
    "Split the downloaded MNIST dataset into two dataloaders ```train``` & ```val```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Download the MNIST dataset\n",
    "mnist_data = datasets.MNIST(root='data/', download=True, transform=transformation)\n",
    "\n",
    "# Number of images in the dataset: 60,000\n",
    "print(len(mnist_data))\n",
    "\n",
    "# Split the dataset into training and validation sets\n",
    "train_data, valid_data = random_split(mnist_data, [50000, 10000]) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create dataloaders for your train and validation data with ```batch size 16``` and ```shuffle = True``` for train loader."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Hint</summary>\n",
    "<br>\n",
    "<a href=\"https://pytorch.org/tutorials/beginner/basics/data_tutorial.html\">DATASETS & DATALOADERS</a>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "4CEUTGaFQI51"
   },
   "outputs": [],
   "source": [
    "###############################################################\n",
    "##           Load the datasets into train/valid              ##\n",
    "##                       dataloaders                         ##\n",
    "###############################################################\n",
    "train_loader = None # Write your code here\n",
    "val_loader = None # Write your code here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HF_40_3rQ5Uv"
   },
   "source": [
    "# Define Model (40 Points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### You need to implement a model with the architecture below:\n",
    "[Input: (BS, 1, 28, 28)] -- > [Conv2D (BS, 16, 28, 28)] --> ReLU --> [MaxPool2D (BS, 16, 14, 14): Residual1]\n",
    "\n",
    "--> [Conv2D (BS, 16, 14, 14)] --> ReLU --> [Conv2D (BS, 16, 14, 14)] --> ReLU --> [Addition with Residual1]\n",
    "\n",
    "--> [MaxPool2D (BS, 16, 7, 7)] --> [Conv2D (BS, 64, 7, 7)] --> [ReLU: Residual2]\n",
    "\n",
    "--> [Conv2D (BS, 64, 7, 7)] --> ReLU --> [Conv2D (BS, 64, 7, 7)] --> ReLU --> [Addition with Residual2]\n",
    "\n",
    "--> [GlobalAveragePooling (BS, 64, 1, 1)] --> [Flatten (BS, 64)] --> [Linear (BS, 10)] --> ReLU --> OUT\n",
    "\n",
    "Here you can see the drawn schematic of the architecture, too: [Link](https://drive.google.com/file/d/1gQzafyxTkuvQr8T-xUncLmedX94LnY_l/view?usp=share_link)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary>Hint</summary>\n",
    "<br>\n",
    "These might be helpful:\n",
    "<br>\n",
    "<a href=\"https://pytorch.org/docs/stable/generated/torch.clone.html\">TORCH.CLONE</a>\n",
    "<br><br>\n",
    "<a href=\"https://pytorch.org/docs/master/generated/torch.nn.AdaptiveAvgPool2d.html#torch.nn.AdaptiveAvgPool2d\">AdaptiveAvgPool2d</a> with output size (1, 1) for global average pooling\n",
    "<br><br>\n",
    "<a href=\"https://arxiv.org/pdf/1512.03385.pdf\">Deep Residual Learning for Image Recognition</a>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "jQfGVj2rQ90G"
   },
   "outputs": [],
   "source": [
    "#####################################\n",
    "##        Define your model        ##\n",
    "##            Your Code            ##\n",
    "#####################################\n",
    "class ResidualClassifier(nn.Module):\n",
    "    def __init__(self):\n",
    "        pass # Write your code here\n",
    "    def forward(self, x):\n",
    "        pass # Write your code here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1jZeKXV9Rbq3"
   },
   "source": [
    "# Train Model (30 Points)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "koMk0TkKRazn"
   },
   "outputs": [],
   "source": [
    "######################################################################\n",
    "##        Instantiate model, define hyper parameters, optimizer,    ##\n",
    "##        loss function and etc                                     ##\n",
    "######################################################################\n",
    "model = ResidualClassifier().to(device)\n",
    "criterion = None # Write your code here\n",
    "optimizer = None # Write your code here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "######################################################################\n",
    "##        Train your model (Complete the code below)                ##\n",
    "######################################################################\n",
    "total_step = len(train_loader)\n",
    "num_epochs = 20\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    for images, labels in train_loader:  \n",
    "        pass # Write your code here\n",
    "\n",
    "    with torch.no_grad():\n",
    "        for images, labels in val_loader:\n",
    "            pass # Write your code here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "_YSWvcEGSVor"
   },
   "outputs": [],
   "source": [
    "##############################################################\n",
    "##          Plot metrics graph for different epochs         ##\n",
    "##                        Your Code                         ##\n",
    "##############################################################\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "LzhCrIFkSE1D"
   },
   "source": [
    "# Test Model (20 Points)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ihixveNISFwE"
   },
   "outputs": [],
   "source": [
    "##################################################\n",
    "##          Test your model on test-set         ##\n",
    "##          and plot confusion matrix           ##\n",
    "##################################################\n",
    "\n",
    "test_data = datasets.MNIST(root='data/', download=True, transform=transformation, train=False)\n",
    "test_loader = DataLoader(test_data, batch_size=64, shuffle=False)\n",
    "print(len(test_data))\n",
    "\n",
    "with torch.no_grad():\n",
    "    pass # Write your code here"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  },
  "vscode": {
   "interpreter": {
    "hash": "bb93c294e1388593f3ce88f4c5e050b65c17fc54b955fa171f33a4b9b3d10054"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
